package memberservice

import (
	"errors"
	"fmt"
	"time"
	"yunj/pkg/global"
	"yunj/pkg/yunj/app/consts"
	"yunj/pkg/yunj/app/enum/redisenum"
	"yunj/pkg/yunj/app/enum/stateenum"
	"yunj/pkg/yunj/app/model"
	"yunj/pkg/yunj/util"

	"github.com/gin-gonic/gin"
	"gorm.io/gorm"
)

// token过期时间
const TOKEN_EXPIRE_TIME = 86400

type login struct {
}

var Login *login

// 登录
// 返回token和token过期时间
func (l *login) LoginHandle(ctx *gin.Context, adminMember *model.AdminMember) (string, int) {
	currTime := time.Now().In(time.Local)
	// 创建token
	token := CreateToken(adminMember, ctx.Request.UserAgent())
	// 记录登录信息
	adminMember.LastLoginIp = ctx.ClientIP()
	adminMember.LastLoginAt = &currTime
	global.MySQL.Save(&adminMember)
	// 登录日志记录
	// 设置Cookie的名称、值、过期时间（秒）、路径、域名、是否仅通过HTTPS传输、是否禁止JavaScript访问
	ctx.SetCookie("token", token, TOKEN_EXPIRE_TIME, "/", "", false, true)
	return token, TOKEN_EXPIRE_TIME
}

// 退出登录
func (l *login) Logout(ctx *gin.Context) {
	token := util.CookieGet(ctx, "token", "")
	if token == "" {
		return
	}
	ClearToken(token)
	// 退出日志记录
}

// 验证token
func (l *login) Verify(ctx *gin.Context) (adminMember *model.AdminMember) {
	token := GetRequestToken(ctx)
	if token == "" {
		return
	}
	// 判断是否已经校验过
	if tokenValue, exists := ctx.Get(token); exists {
		// 判断是否为member
		if tokenValue, ok := tokenValue.(*model.AdminMember); ok {
			adminMember = tokenValue
			return
		}
	}
	// 验证token
	if adminMember = VerifyToken(token, ctx.Request.UserAgent(), !util.IsInnerSystemRequest(ctx)); adminMember == nil {
		return
	}

	ctx.Set(token, adminMember)
	return
}

// 创建token
// ttl 不传入默认缓存1-2天
func CreateToken(adminMember *model.AdminMember, userAgent string, ttl ...int) (token string) {
	if adminMember == nil || adminMember.Id <= 0 {
		return
	}
	// 生成token
	token = GenerateToken(adminMember, userAgent)
	// 保存token
	SaveToken(token, adminMember.Id, ttl...)
	return
}

// 验证token
// isRenewal 是否延长token有效期
func VerifyToken(token string, userAgent string, isRenewal bool, ttl ...int) (adminMember *model.AdminMember) {
	if token == "" {
		return
	}
	// 根据token获取member
	tokenMember := GetMemberByToken(token, ttl...)
	if tokenMember == nil || tokenMember.Id <= 0 {
		return
	}
	// 根据member生成token
	_token := GenerateToken(tokenMember, userAgent)
	if _token == "" || _token != token {
		return
	}
	// 判断是否延长有效期
	if isRenewal {
		SaveToken(token, tokenMember.Id, ttl...)
	}
	adminMember = tokenMember
	return
}

// 获取请求token
func GetRequestToken(ctx *gin.Context) (token string) {
	token = util.CookieGet(ctx, "token", "")
	if token == "" {
		token = ctx.GetHeader("token")
	}
	return
}

// 生成token
func GenerateToken(adminMember *model.AdminMember, userAgent string) (token string) {
	if adminMember.Id <= 0 {
		return
	}
	token = util.Md5(fmt.Sprintf("%v%s%s%s%s%s", adminMember.Id, adminMember.Username, adminMember.PasswordSalt, adminMember.PasswordHash, userAgent, consts.ADMIN_TOKEN_SALT))
	return
}

// 通过token获取用户信息
// ttl不传入，默认不延长token过期时间
func GetMemberByToken(token string, ttl ...int) (member *model.AdminMember) {
	if token == "" {
		return
	}
	redisKey := GetMemberIdRedisKey(token)
	memberId, err := redisKey.GetCacheUint64(ttl...)
	if err != nil {
		global.Logger.WithError(err).Errorf("memberId缓存获取异常！token=%s", token)
		return
	}
	if memberId <= 0 {
		global.Logger.WithError(err).Errorf("memberId缓存值异常！token=%s", token)
		return
	}
	member = GetMemberById(memberId)
	return
}

// 通过id获取用户信息
func GetMemberById(id uint64) (member *model.AdminMember) {
	if id <= 0 {
		return
	}
	var memberData model.AdminMember
	err := global.MySQL.Preload("Roles").Where("id = ? and state = ?", id, stateenum.NORMAL).First(&memberData).Error
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			global.Logger.WithError(err).Error(fmt.Sprintf("memberId=%d数据异常！", id))
			return
		} else {
			global.Logger.WithError(err).Error(fmt.Sprintf("memberId=%d数据查询异常！", id))
			return
		}
	}
	if len(memberData.GetOwnRoles()) <= 0 {
		global.Logger.WithError(err).Error(fmt.Sprintf("memberId=%d映射的管理员角色数据查询异常！", id))
		return
	}
	member = &memberData
	return
}

// 保存token
// ttl不传入，默认缓存1-2天
func SaveToken(token string, memberId uint64, ttl ...int) {
	expire := 0
	if len(ttl) > 0 {
		expire = ttl[0]
	}
	redisKey := GetMemberIdRedisKey(token)
	redisKey.SetCache(memberId, expire)
}

// 清除token
func ClearToken(token string) {
	redisKey := GetMemberIdRedisKey(token)
	redisKey.DelCache()
}

// 获取member id的redis key实例
func GetMemberIdRedisKey(token string) *redisenum.Key {
	return redisenum.KeyByValue(redisenum.KEY_ADMIN_MEMBER_ID_BY_TOKEN).SetArgs(token)
}
